package service

import (
	"context"
	"errors"
	"iris-wms/apps/admin/web/dto/din"
	"iris-wms/apps/admin/web/dto/dout"
	"iris-wms/common/model"
	"iris-wms/common/pkg/db"
	"iris-wms/common/pkg/db/dao"
	"iris-wms/common/tools/consts"

	"gorm.io/gorm"
)

var _ WmsBillMoveService = (*wmsBillMoveService)(nil)

type WmsBillMoveService interface {
	Create(din.WmsBillIn) error
	Update(din.WmsBillIn) error
	DeleteByIdList([]uint) error
	GetTable(din.WmsBillTableIn) ([]*dout.WmsBillOut, int64, error)
	FindByCode(string, consts.WorkType) (*dout.WmsBillOut, error)
}

func NewWmsBillMoveService() *wmsBillMoveService {
	return &wmsBillMoveService{}
}

type wmsBillMoveService struct{}

func (impl *wmsBillMoveService) Create(in din.WmsBillIn) (err error) {
	inventoryService := NewWmsInventoryService()
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		srcInvt := make([]*model.WmsInventory, 0)
		destInvt := make([]*model.WmsInventory, 0)
		for _, v := range in.GoodsList {
			// 检查库存
			inventory, err := inventoryService.QueryInventory(v.GoodsId, v.SrcWid)
			if err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return consts.Wms_Bill_Ountbound_Not_Qty
				}
				return err
			}
			if inventory.SumQty < v.Qty {
				return consts.Wms_Bill_Ountbound_Not_Qty
			}

			v.BillId = in.Bill.ID
			srcInvt = append(srcInvt, &model.WmsInventory{
				BillId:  in.Bill.ID,
				Wid:     v.SrcWid,
				GoodsId: v.GoodsId,
				Qty:     v.Qty,
				Price:   v.Price,
				Mark:    consts.BillMarkOut,
				IsGift:  v.IsGift,
			})
			destInvt = append(destInvt, &model.WmsInventory{
				BillId:  in.Bill.ID,
				Wid:     v.DestWid,
				GoodsId: v.GoodsId,
				Qty:     v.Qty,
				Price:   v.Price,
				Mark:    consts.BillMarkIn,
				IsGift:  v.IsGift,
			})
		}
		if err = tx.CreateInBatches(&in.GoodsList, len(in.GoodsList)).Error; err != nil {
			return err
		}
		// invt
		if len(srcInvt) > 0 && len(destInvt) > 0 {
			if err = tx.CreateInBatches(&srcInvt, len(srcInvt)).Error; err != nil {
				return err
			}
			if err = tx.CreateInBatches(&destInvt, len(destInvt)).Error; err != nil {
				return err
			}
		}
		//
		if in.Bill.WorkType == consts.WorkType2 && len(in.LogisticsList) > 0 {
			for _, v := range in.LogisticsList {
				v.BillId = in.Bill.ID
			}
			if err = tx.CreateInBatches(&in.LogisticsList, len(in.LogisticsList)).Error; err != nil {
				return err
			}
		}
		return nil
	})
	return
}

func (impl *wmsBillMoveService) Update(in din.WmsBillIn) (err error) {
	inventoryService := NewWmsInventoryService()
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Save(&in.Bill).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsBillGoods{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if err = tx.Unscoped().Delete(&model.WmsInventory{}, "bill_id=?", in.Bill.ID).Error; err != nil {
			return err
		}
		if len(in.GoodsList) == 0 {
			return consts.Wms_Bill_Not_Goods
		}
		for _, v := range in.GoodsList {
			inventory, err := inventoryService.QueryInventory(v.GoodsId, v.SrcWid)
			if err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					return consts.Wms_Bill_Ountbound_Not_Qty
				}
				return err
			}
			if inventory.SumQty < v.Qty {
				return consts.Wms_Bill_Ountbound_Not_Qty
			}

			v.BillId = in.Bill.ID
			if err = tx.Save(&v).Error; err != nil {
				return err
			}
			// src
			if err = tx.Save(&model.WmsInventory{
				BillId:  in.Bill.ID,
				Wid:     v.SrcWid,
				GoodsId: v.GoodsId,
				Qty:     v.Qty,
				Price:   v.Price,
				Mark:    consts.BillMarkOut,
				IsGift:  v.IsGift,
			}).Error; err != nil {
				return err
			}
			// dest
			if err = tx.Save(&model.WmsInventory{
				BillId:  in.Bill.ID,
				Wid:     v.DestWid,
				GoodsId: v.GoodsId,
				Qty:     v.Qty,
				Price:   v.Price,
				Mark:    consts.BillMarkIn,
				IsGift:  v.IsGift,
			}).Error; err != nil {
				return err
			}
		}
		//
		if in.Bill.WorkType == consts.WorkType2 && len(in.LogisticsList) > 0 {
			if err = tx.Unscoped().Delete(&model.WmsBillLogistics{}, "bill_id=?", in.Bill.ID).Error; err != nil {
				return err
			}
			for _, v := range in.LogisticsList {
				v.BillId = in.Bill.ID
				if err = tx.Save(&v).Error; err != nil {
					return err
				}
			}
		}
		return nil
	})
	return
}

func (impl *wmsBillMoveService) DeleteByIdList(idList []uint) (err error) {
	err = db.GDB1.Transaction(func(tx *gorm.DB) error {
		if err = tx.Delete(&model.WmsBill{}, "id in ?", idList).Error; err != nil {
			return err
		}
		if err = tx.Model(&model.WmsInventory{}).Where("bill_id in ?", idList).Update("status", 0).Error; err != nil {
			return err
		}
		return nil
	})
	return
}

func (impl *wmsBillMoveService) GetTable(in din.WmsBillTableIn) (rows []*dout.WmsBillOut, total int64, err error) {
	q := dao.Use(db.GDB1).WmsBillOut
	do := q.WithContext(context.Background())

	if in.Word != "" {
		do = do.Where(do.Where(q.Code.Eq(in.Word)))
	}
	rows, total, err = do.Where(q.WorkType.Eq(int8(consts.WorkType3))).
		Preload(q.Creator).
		Preload(q.Editor).
		Preload(q.IoStatusDict).Preload(q.IoTypeDict).
		Preload(q.Supplier).Preload(q.Customer).
		Preload(q.GoodsList.Goods.Cate).
		Preload(q.GoodsList.Warehouse).
		Order(q.CreatedAt.Desc()).
		FindByPage(in.Offset(), in.PageSize)
	return
}

func (impl *wmsBillMoveService) FindByCode(code string, workType consts.WorkType) (bill *dout.WmsBillOut, err error) {
	q := dao.Use(db.GDB1).WmsBillOut
	do := q.WithContext(context.Background())
	bill, err = do.Where(q.Code.Eq(code), q.WorkType.Eq(int8(consts.WorkType3))).
		Preload(q.Creator).
		Preload(q.Editor).
		Preload(q.IoStatusDict).Preload(q.IoTypeDict).
		Preload(q.GoodsList.Goods.Cate).
		Preload(q.GoodsList.SrcWarehouse).
		Preload(q.GoodsList.DestWarehouse).
		First()
	return
}
